home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / QuickDraw / Restore Screen Cluts / EmergMem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  9.4 KB  |  277 lines  |  [TEXT/MPS ]

  1. /******************************************************************************\
  2. *
  3. * Apple Macintosh Developer Technical Support
  4. *
  5. * Source code for the emergency memory routines
  6. *
  7. * Program: ColorReset
  8. * File:    EmergMem.c
  9. *
  10. * by:      Forrest Tanaka
  11. *
  12. * Copyright © 1988-1991 Apple Computer, Inc.
  13. * All rights reserved.
  14. *
  15. \******************************************************************************/
  16.     
  17.  
  18. /******************************************************************************\
  19. * Header Files
  20. \******************************************************************************/
  21.  
  22. #ifndef THINK_C
  23. #include <OSUtils.h>
  24. #endif
  25.  
  26. #include "EmergMem.h"
  27.  
  28.  
  29. /******************************************************************************\
  30. * Constants
  31. \******************************************************************************/
  32.  
  33. #define kEmergMemSize 32768 /* Number of bytes of emergency mem to allocate */
  34. #define kMemoryMargin 32768 /* “significant amount” amount of free mem */
  35.  
  36.  
  37. /******************************************************************************\
  38. * Global Variables
  39. \******************************************************************************/
  40.  
  41. Handle gEmergMem; /* Handle to block of emergency memory */
  42.  
  43.  
  44. /******************************************************************************\
  45. * Private: AppGrowZone - Custom grow-zone procedure
  46. *
  47. * This is a very basic grow zone procedure.  My application keeps a reserve
  48. * handle of memory in case the Memory Manager gets a request for some memory
  49. * that is not available in my heap.  If memory were to get tight (<32k or so),
  50. * the Toolbox could crash the system.  This grow-zone proc tries to thwart that
  51. * possibility by releasing the 32K block of emergency memory if it hasn’t been
  52. * released already and if the amount of memory requested is less than 32K.
  53. * Hopefully, that’s enough to satisfy the memory request.
  54. *
  55. * There are three conditions in which the emergency memory isn’t freed.  If the
  56. * emergency memory is already free, obviously there isn’t much that can be done.
  57. * If the emergency memory is equal to GZSaveHnd, then it was the reallocation of
  58. * emergency memory that caused this grow-zone proc to be called.  So it doesn’t
  59. * make much sense to free it in that case.  If the size of the memory request is
  60. * more than the size of emergency memory, then I don’t bother to free emergency
  61. * memory because I assume that the toolbox handles such huge requests for memory
  62. * properly.  Warning: that isn’t always a good assumption, but that’s not my
  63. * fault.
  64. *
  65. *     WARNING: Register A5 might not be valid when grow-zone procedures
  66. *     are called. Read Technical Note #136 and 208.
  67. *
  68. * The "cbNeeded" parameter is the number of bytes that the Memory Manager needs
  69. * to fulfill the memory request it had received.  The number of bytes actually
  70. * freed by AppGrowZone is returned.
  71. \******************************************************************************/
  72.  
  73. static pascal long AppGrowZone(
  74.     Size cbNeeded) /* Number of bytes needed by Memory Manager */
  75. {
  76.     long theA5;       /* Value of A5 when AppGrowZone is called */
  77.     long amountFreed; /* Number of bytes freed up */
  78.  
  79.     /* Remember the current value of A5 */
  80.     theA5 = SetCurrentA5();
  81.  
  82.     /* Free emergency memory if possible */
  83.     if (!NoEmergMem() && (gEmergMem != GZSaveHnd()) &&
  84.             (cbNeeded <= kEmergMemSize))
  85.     {
  86.         EmptyHandle( gEmergMem );
  87.         amountFreed = kEmergMemSize;
  88.     }
  89.     else
  90.         amountFreed = 0;
  91.  
  92.     /* Restore A5 */
  93.     (void)SetA5( theA5 );
  94.  
  95.     /* Return number of bytes freed */
  96.     return amountFreed;
  97. }
  98.  
  99.  
  100. /******************************************************************************\
  101. * Public: InstallAppGZ
  102. *
  103. * Installing our custom grow-zone procedure simply involves calling SetGrowZone
  104. * with the address of our custom grow-zone procedure.
  105. \******************************************************************************/
  106.  
  107. void InstallAppGZ()
  108. {
  109.     SetGrowZone( (GrowZoneProcPtr)&AppGrowZone );
  110. }
  111.  
  112.  
  113. /******************************************************************************\
  114. * Public: DeinstallAppGZ
  115. *
  116. * Passing NIL to SetGrowZone is all that’s needed to tell the Memory Manager not
  117. * to call a grow-zone procedure whenenver memory requests can’t be satisfied.
  118. \******************************************************************************/
  119.  
  120. void DeinstallAppGZ()
  121. {
  122.     SetGrowZone( nil );
  123. }
  124.  
  125.  
  126. /******************************************************************************\
  127. * Public: InitEmergMem
  128. *
  129. * If the block of emergency memory couldn’t be allocated, then we’re probably in
  130. * some pretty big trouble.  But InitEmergMem tries to deal as best it can in
  131. * that case by allocating an empty handle; that is, it allocates a master
  132. * pointer that’s set to NIL.  That leaves it up to the rest of the application
  133. * to decide what to do.
  134. \******************************************************************************/
  135.  
  136. void InitEmergMem()
  137. {
  138.     /* Allocate the block of emergency memory */
  139.     gEmergMem = NewHandle( kEmergMemSize );
  140.     if (gEmergMem == nil)
  141.         /* Couldn’t allocate emergency memory; just allocate an empty handle */
  142.         gEmergMem = NewEmptyHandle();
  143.  
  144.     /* Now that emergency memory is initialized, can install grow zone proc */
  145.     InstallAppGZ();
  146. }
  147.  
  148.  
  149. /******************************************************************************\
  150. * Public: RecoverEmergMem
  151. *
  152. * ReallocHandle takes an existing empty handle (handle whose master pointer is
  153. * nil) and allocates a block of memory for it.  Perfect for this job!
  154. \******************************************************************************/
  155.  
  156. void RecoverEmergMem()
  157. {
  158.     ReallocHandle( gEmergMem, kEmergMemSize );
  159. }
  160.  
  161.  
  162. /******************************************************************************\
  163. * Public: FailLowMemory
  164. *
  165. * PurgeSpace is used to determine how much free memory there’d be in the heap if
  166. * all purgeable blocks were purged.  If this amount is less than the amount
  167. * needed, or if there isn’t any emergency memory, true is returned.
  168. \******************************************************************************/
  169.  
  170. Boolean FailLowMemory(
  171.     long memRequest) /* Amount of memory to check */
  172. {
  173.     long total;  /* Total amount of free memory if heap was purged */
  174.     long contig; /* Max amount of free contiguous memory if heap was purged */
  175.  
  176.     PurgeSpace( /*<*/&total, /*<*/&contig );
  177.     return (total < (memRequest + kMemoryMargin)) || NoEmergMem();
  178. }
  179.  
  180.  
  181. /******************************************************************************\
  182. * Public: NoEmergMem
  183. *
  184. * We check on the handle and the master pointer of gEmergMem to see if the
  185. * emergency memory block has been emptied by AppGrowZone, or was never allocated
  186. * in the first place.  StripAddress is called because we’re comparing the master
  187. * pointer of the emergency memory handle against zero, and the upper byte of
  188. * master pointers can be non-zero if the machine is booted in 24-bit addressing
  189. * mode.
  190. \******************************************************************************/
  191.  
  192. Boolean NoEmergMem()
  193. {
  194.     /* Empty handle means no emergency memory */
  195.     return (gEmergMem == nil) || (StripAddress( *gEmergMem ) == nil);
  196. }
  197.  
  198.  
  199. /******************************************************************************\
  200. * Public: NewHandleMargin
  201. *
  202. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  203. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  204. * deinstall the grow-zone proc temporarily just before I allocate the memory.
  205. \******************************************************************************/
  206.  
  207. Handle NewHandleMargin(
  208.     Size    requestedSize, /* Number of bytes requested to be allocated */
  209.     Boolean appHeapAlloc,  /* Allocate in app heap or system heap? */
  210.     Boolean clearMem)      /* Clear allocated memory or leave it alone? */
  211. {
  212.     Handle aHandle; /* Handle to newly-allocated memory */
  213.  
  214.     if (FailLowMemory( requestedSize ))
  215.         aHandle = nil;
  216.     else
  217.     {
  218.         /* We handle memFullErr properly, so don’t need grow-zone proc */
  219.         DeinstallAppGZ();
  220.  
  221.         /* Allocate the memory with the requested options */
  222.         if (!appHeapAlloc && clearMem)
  223.             aHandle = NewHandleSysClear( requestedSize );
  224.         else if (!appHeapAlloc)
  225.             aHandle = NewHandleSys( requestedSize );
  226.         else if (clearMem)
  227.             aHandle = NewHandleClear( requestedSize );
  228.         else
  229.             aHandle = NewHandle( requestedSize );
  230.  
  231.         /* Install the grow-zone proc again */
  232.         InstallAppGZ();
  233.     }
  234.  
  235.     return aHandle;
  236. }
  237.  
  238.  
  239. /******************************************************************************\
  240. * Public: NewPtrMargin
  241. *
  242. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  243. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  244. * disconnect the grow-zone proc temporarily just before I allocate the memory.
  245. \******************************************************************************/
  246.  
  247. Ptr NewPtrMargin(
  248.     Size    requestedSize, /* Number of bytes requested to be allocated */
  249.     Boolean appHeapAlloc,  /* Allocate in app heap or system heap? */
  250.     Boolean clearMem)      /* Clear allocated memory or leave it alone? */
  251. {
  252.     Ptr aPtr; /* Pointer to newly-allocated memory */
  253.  
  254.     if (FailLowMemory( requestedSize ))
  255.         aPtr = nil;
  256.     else
  257.     {
  258.         /* We handle memFullErr properly, so don’t need grow-zone proc */
  259.         DeinstallAppGZ();
  260.  
  261.         /* Allocate the memory with the requested options */
  262.         if (!appHeapAlloc && clearMem)
  263.             aPtr = NewPtrSysClear( requestedSize );
  264.         else if (! appHeapAlloc)
  265.             aPtr = NewPtrSys( requestedSize );
  266.         else if (clearMem)
  267.             aPtr = NewPtrClear( requestedSize );
  268.         else
  269.             aPtr = NewPtr( requestedSize );
  270.  
  271.         /* Connect up the grow-zone proc again */
  272.         InstallAppGZ();
  273.     }
  274.  
  275.     return aPtr;
  276. }
  277.